using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Reflection;

#if !NUNIT
using Microsoft.VisualStudio.TestTools.UnitTesting;
#else
using NUnit.Framework;
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using TestCleanup = NUnit.Framework.TearDownAttribute;
#endif

namespace DeflateLib.Tests
{
    [TestClass]
    public class LZ773ByteHashTests
    {
        [TestMethod]
        public void Test3ByteHashNoHits()
        {
            LZ773ByteHashProxy proxy = new LZ773ByteHashProxy(10);
            LZ773ByteHashProxy.Iterator it = proxy.Get(new byte[] { 0, 0, 0 }, 0);
            Assert.IsNull(it);
        }

        [TestMethod]
        public void Test3ByteHash1Hit()
        {
            byte[] data = {1,2,3,4,5,6,7,8,9,10};

            LZ773ByteHashProxy proxy = new LZ773ByteHashProxy(10);
            for (int i = 0; i < 7; i++)
            {
                proxy.Push(data, i);
            }
            LZ773ByteHashProxy.Iterator it = proxy.Get(new byte[] { 1, 2, 3 }, 0);
            Assert.IsNotNull(it);
            Assert.IsTrue(it.MoveNext());
            Assert.AreEqual(7, it.CurrentDistance);
            Assert.IsFalse(it.MoveNext());
        }


        [TestMethod]
        public void Test3ByteHashNoHitsRound()
        {
            byte[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };

            LZ773ByteHashProxy proxy = new LZ773ByteHashProxy(10);
            for (int i = 0; i < 11; i++)
            {
                proxy.Push(data, i);
            }
            LZ773ByteHashProxy.Iterator it = proxy.Get(new byte[] { 1, 2, 3 }, 0);
            Assert.IsNull(it);
        }

        [TestMethod]
        public void Test3ByteHashAllHits()
        {
            byte[] data = { 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3 };

            LZ773ByteHashProxy proxy = new LZ773ByteHashProxy(11);
            for (int i = 0; i < 10; i++)
            {
                proxy.Push(data, i);
            }
            LZ773ByteHashProxy.Iterator it = proxy.Get(new byte[] { 2, 1, 3 }, 0);
            Assert.IsNotNull(it);
            List<int> result = new List<int>();
            while(it.MoveNext())
                result.Add(it.CurrentDistance);

            Assert.AreEqual(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, result.ToArray());
        }

        class LZ773ByteHashProxy
        {
            static Type targetType = Type.GetType("DeflateLib.LZ773ByteHash,DeflateLib", true);
            object target;

            public LZ773ByteHashProxy(int size)
            {
                this.target = targetType.GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(int) }, null).
                    Invoke(new object[] { size });
            }

            public void Clear()
            {
                targetType.InvokeMember("Clear", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
                    null, target, null);
            }

            public Iterator Get(byte[] data, int offset)
            {
                object obj =
                    targetType.InvokeMember("Get", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
                    null, target, new object[] {data, offset});

                if (obj == null) return null; else return new Iterator(obj);
            }

            public void Push(byte[] data, int offset)
            {
                targetType.InvokeMember("Push", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
                    null, target, new object[] { data, offset });
            }

            public class Iterator
            {
                static Type targetType = LZ773ByteHashProxy.targetType.GetNestedType("Iterator", BindingFlags.NonPublic);
                object target;

                internal Iterator(object target)
                {
                    this.target = target;
                }

                public int CurrentDistance
                {
                    get
                    {
                        return (int)targetType.InvokeMember("CurrentDistance", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty,
                      null, target, null);
                    }
                }

                internal bool MoveNext()
                {
                        return (bool)targetType.InvokeMember("MoveNext", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
                      null, target, null);
                }
            }
        }

        
    }
}
